﻿using Kimd.Common;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace Kimd.Communication
{
    [Serializable]
    public class SerialPortHelper : ISerialPort, IDisposable, ICommunicationShow
    {
        #region Properties
        private bool IsDisposed { get; set; }
        private object lockObj = new object();
        private SerialPort _SerialPort;
        private ManualResetEvent ReceiveFinishEvent = new ManualResetEvent(true);
        public bool IsReceivedData { get; set; }
        /// <summary>
        /// 波特率
        /// </summary>
        public int BaudRate
        {
            get { return Config.BaudRate; }
        }
        /// <summary>
        /// 数据位
        /// </summary>
        public int DataBits
        {
            get { return Config.DataBits; }
        }
        /// <summary>
        /// 串口号
        /// </summary>
        public string PortName
        {
            get { return Config.PortName; }
        }
        /// <summary>
        /// 验证位
        /// </summary>
        public Parity Parity
        {
            get { return Config.Parity; }
        }
        /// <summary>
        /// 停止位
        /// </summary>
        public StopBits StopBits
        {
            get { return Config.StopBits; }
        }
        /// <summary>
        /// 接收解码
        /// </summary>
        [XmlIgnore]
        public Encoding ReceiveEncoding
        {
            get
            {
                Encoding encoding = Encoding.Default;
                switch (Config.ReceiveEncoding)
                {
                    case EncodingEnumStr.ASCII:
                        encoding = Encoding.ASCII;
                        break;
                    case EncodingEnumStr.Default:
                        encoding = Encoding.Default;
                        break;
                    case EncodingEnumStr.UTF8:
                        encoding = Encoding.UTF8;
                        break;
                    case EncodingEnumStr.UTF32:
                        encoding = Encoding.UTF32;
                        break;
                    case EncodingEnumStr.Unicode:
                        encoding = Encoding.Unicode;
                        break;
                }
                return encoding;
            }
        }
        /// <summary>
        /// 发送解码
        /// </summary>
        [XmlIgnore]
        public Encoding SendEncoding
        {
            get
            {
                Encoding encoding = Encoding.Default;
                switch (Config.SendEncoding)
                {
                    case EncodingEnumStr.ASCII:
                        encoding = Encoding.ASCII;
                        break;
                    case EncodingEnumStr.Default:
                        encoding = Encoding.Default;
                        break;
                    case EncodingEnumStr.UTF8:
                        encoding = Encoding.UTF8;
                        break;
                    case EncodingEnumStr.UTF32:
                        encoding = Encoding.UTF32;
                        break;
                    case EncodingEnumStr.Unicode:
                        encoding = Encoding.Unicode;
                        break;
                }
                return encoding;
            }
        }
        /// <summary>
        /// 配置文件
        /// </summary>
        public SerialPortConfig Config { get; set; } = new SerialPortConfig();
        /// <summary>
        /// 接收间隔时间ms
        /// </summary>
        public int ReceiveIntervalTime { get; set; } = 100;
        /// <summary>
        /// 是否采用读取间隔设置
        /// </summary>
        public bool IsReceiveIntervalEnable { get; set; } = true;
        /// <summary>
        /// 接收到信息
        /// </summary>
        public string ReceiveDataStr = string.Empty;
        /// <summary>
        /// 接收到信息
        /// </summary>
        public byte[] ReceiveDataByte = new byte[] { };
        /// <summary>
        /// 打开状态
        /// </summary>
        [XmlIgnore]
        public bool IsOpen => _SerialPort.IsOpen;
        #endregion

        #region Event

        /// <summary>
        /// 连接事件
        /// </summary>
        public event EventHandler Connected;
        protected void OnConnected()
        {
            if (Connected != null)
            {
                Connected(this, new EventArgs());
            }
        }
        /// <summary>
        /// 断开事件
        /// </summary>
        public event EventHandler Disconnected;
        protected void OnDisconnected()
        {
            if (Disconnected != null)
                Disconnected(this, new EventArgs());
        }
        /// <summary>
        /// 接收信息事件
        /// </summary>
        public event EventHandler<DatagramReceivedEventArgs<byte[]>> DatagramReceived;
        protected void OnDatagramReceived(DatagramReceivedEventArgs<byte[]> e)
        {
            if (DatagramReceived != null)
                DatagramReceived(this, e);
        }
        /// <summary>
        /// 通讯信息显示事件
        /// </summary>
        public event Action<SerialPortHelper, string> CommRunDisplay;
        protected void OnCommRunDisplay(SerialPortHelper sp, string str)
        {
            if (CommRunDisplay != null)
                CommRunDisplay(sp, str);
        }
        #endregion

        #region Contructor
        public SerialPortHelper()
        {
            _SerialPort = new SerialPort();
            _SerialPort.DataReceived -= _SerialPort_DataReceived;
            _SerialPort.DataReceived += _SerialPort_DataReceived;
            //SendCmd = new SerializableDictionary<string, string>();
        }

        public SerialPortHelper(string name) : this()
        {
            Name = name;
        }
        public SerialPortHelper(string portName, int baudRate, string name = "",
         Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One,
         bool receiveIntervalEnable = true, int receiveIntervalTime = 100) : this()
        {

            //this.PortName = portName;
            //this.BaudRate = baudRate;
            //this.Parity = parity;
            //this.DataBits = dataBits;
            //this.StopBits = stopBits;
            this.Name = name;
            Config.BaudRate = baudRate;
            Config.Parity = parity;
            Config.DataBits = dataBits;
            Config.StopBits = stopBits;
            Config.PortName = portName;
            this.IsReceiveIntervalEnable = receiveIntervalEnable;
            this.ReceiveIntervalTime = receiveIntervalTime;
        }
        ~SerialPortHelper()
        {
            _SerialPort.DataReceived -= _SerialPort_DataReceived;
        }
        #endregion

        #region Methods
        /// <summary>
        /// 异步接收事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (IsReceiveIntervalEnable && ReceiveIntervalTime > 0)
                Thread.Sleep(ReceiveIntervalTime);
            byte[] readBuffer = new byte[_SerialPort.BytesToRead];
            _SerialPort.Read(readBuffer, 0, readBuffer.Length);
            ReceiveDataByte = readBuffer;
            ReceiveDataStr = ReceiveEncoding.GetString(readBuffer);
            DatagramReceivedEventArgs<byte[]> data = new DatagramReceivedEventArgs<byte[]>(Name, ReceiveDataStr, ReceiveDataByte);
            OnDatagramReceived(data);
            OnCommRunDisplay(this, ReceiveDataStr);
            IsReceivedData = true;
            ReceiveFinishEvent.Set();
            //OnCommRunDisplay($"{Name}<--:{ReceiveDataStr}");
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            try
            {
                if (_SerialPort != null && IsOpen)
                {
                    _SerialPort.Close();
                }
                ResetReceiveValue();
                OnDisconnected();
                OnCommRunDisplay(this, $"{Name}串口关闭成功");
                return true;
            }
            catch (Exception ex)
            {
                OnCommRunDisplay(this, $"{Name}串口关闭失败，失败原因:{ex.Message}");
                MessageBox.Show(ex.Message);
            }
            return false;
        }
        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            bool result = false;
            if (_SerialPort != null)
            {
                try
                {
                    if (IsOpen)
                        _SerialPort.Close();
                    _SerialPort.PortName = PortName;
                    _SerialPort.BaudRate = BaudRate;
                    _SerialPort.DataBits = DataBits;
                    _SerialPort.Parity = Parity;
                    _SerialPort.Encoding = SendEncoding;
                    _SerialPort.StopBits = StopBits;
                    _SerialPort.Open();
                    result = true;
                    ResetReceiveValue();
                    OnConnected();
                    OnCommRunDisplay(this, $"{Name}串口打开成功");
                }
                catch (Exception ex)
                {
                    string str = $"{Name}串口打开失败!,失败原因:{ex.Message}";
                    OnCommRunDisplay(this, str);
                    MessageBox.Show(str);
                }
            }
            return result;
        }
        protected void ResetReceiveValue()
        {
            IsReceivedData = false;
            ReceiveFinishEvent.Set();
            ReceiveDataByte = new byte[] { };
            ReceiveDataStr = string.Empty;
        }
        protected void ResetSendValue()
        {
            IsReceivedData = false;
            ReceiveFinishEvent.Reset();
            ReceiveDataByte = new byte[] { };
            ReceiveDataStr = string.Empty;
        }
        /// <summary>
        /// 发送信息,返回发送状态,接收结果
        /// </summary>
        /// <param name="send"></param>
        /// <param name="receive"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public bool Send(byte[] send, out string receive, int timeout = 500)
        {
            bool result = false;
            receive = string.Empty;
            if (Send(send))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 发送信息,返回发送状态,接收结果
        /// </summary>
        /// <param name="send"></param>
        /// <param name="receive"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public bool Send(byte[] send, out byte[] receive, int timeout = 500)
        {
            bool result = false;
            receive = new byte[] { };
            if (Send(send))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 发送信息,返回发送状态
        /// </summary>
        /// <param name="send"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public bool Send(byte[] send)
        {
            bool result = false;
            try
            {
                if (_SerialPort != null && IsOpen)
                {
                    ResetSendValue();
                    _SerialPort.Write(send, 0, send.Length);
                    OnCommRunDisplay(this, $" -->: {send.ByteArrayToString()}");
                    result = true;
                }
            }
            catch (Exception ex)
            {
                OnCommRunDisplay(this, $"{Name}串口发送失败,发送内容:{send.ByteArrayToString()}");
                ResetReceiveValue();
            }
            return result;
        }
        /// <summary>
        /// 发送信息,返回发送状态,接收结果
        /// </summary>
        /// <param name="send"></param>
        /// <param name="receive"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public bool Send(string send, out string receive, int timeout = 500)
        {
            bool result = false;
            receive = string.Empty;
            if (Send(send))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 发送信息,返回发送状态,接收结果
        /// </summary>
        /// <param name="send"></param>
        /// <param name="receive"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public bool Send(string send, out byte[] receive, int timeout = 500)
        {
            bool result = false;
            receive = new byte[] { };
            if (Send(send))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 发送信息,返回发送状态
        /// </summary>
        /// <param name="send"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public bool Send(string send)
        {
            bool result = false;
            try
            {
                if (_SerialPort != null && IsOpen)
                {
                    ResetSendValue();
                    _SerialPort.Write(send);
                    OnCommRunDisplay(this, $" -->: {send}");
                    result = true;
                }
            }
            catch (Exception ex)
            {
                OnCommRunDisplay(this, $"{Name}串口发送失败,发送内容:{send}");
                ResetReceiveValue();
            }
            return result;
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!this.IsDisposed)
            {
                if (disposing)
                {
                    try
                    {
                        if (_SerialPort != null)
                            _SerialPort.Dispose();
                    }
                    catch
                    {
                    }
                    IsDisposed = true;
                }
            }

        }
        #endregion

        #region ICommunicationShow
        public CommTypeEnum CommType { get; } = CommTypeEnum.SerialPort;
        /// <summary>
        /// 指令合集
        /// </summary>
        [XmlIgnore]
        public SerializableDictionary<string, string> SendCmd
        {
            get
            {
                return Config.SendCmd;
            }
            set
            {
                Config.SendCmd = value;
            }
        }

        public string Name { get; set; }=string.Empty;

        public bool IsOpenFlag => IsOpen;

        public object GetConfig()
        {
            return Config;
        }

        public Form GetDebugForm()
        {
            return new CommunicationPage(this);
        }

        public List<string> GetShowList()
        {
            return new List<string>();
        }

        public string GetTreeNodeShowStr()
        {
            return $"{Name}(串口)({PortName}:{BaudRate})";
        }

        public void SetConfig(object config)
        {
            if (config is SerialPortConfig)
            {
                Config = config as SerialPortConfig;
            }
        }
        public void SetOpenOrClose(OpenOrClose openOrClose)
        {
            switch (openOrClose)
            {
                case OpenOrClose.Open:
                    Open();
                    break;
                case OpenOrClose.Close:
                    Close();
                    break;
            }
        }
        #endregion
    }

    #region Enum
    public enum PortBaudRate
    {
        RB1200 = 1200,
        RB2400 = 2400,
        RB4800 = 4800,
        RB9600 = 9600,
        RB14400 = 14400,
        RB19200 = 19200,
        RB38400 = 38400,
        RB56000 = 56000,
        RB57600 = 57600,
        RB115200 = 115200,
    }
    public enum PortDataBits
    {
        Bit5 = 5,
        Bit6 = 6,
        Bit7 = 7,
        Bit8 = 8
    }
    #endregion
}
